module("utils", package.seeall)

function clone(t)
	if type(t) ~= "table" then return t end
	local r = {}
	for k, v in pairs(t) do
		r[k] = clone(v)
	end
	return r
end

check_mt = {
	__index = function (tbl, key)
		local v = rawget(tbl, key)
		assert(v, "invalid key: "..key)
		return v
	end
}

slots = {
	head = true,
	neck = true,
	shoulders = true,
	back = true,
	chest = true,
	wrists = true,
	mainhand = true,
	hands = true,
	waist = true,
	legs = true,
	feet = true,
	finger = true,
	trinket = true,
	
	offhand = true,
	shield = true,
	
	wand = true,
	ranged = true,
	idol = true,
	libram = true,
	totem = true,
}

local BASE_INFO = {
	-- reverse engineered via playerdb_info
	dodge = {
		WARRIOR = { 0.37, 1.58, 0.72, -17.84, 0.91, }, -- 1.4, 1.7, 32.4, 132.5
		PALADIN = { 0.30, 1.12, 0.47, -8.72, 0.80, }, -- 1.0, 0.6, 24.3, 102.2
		SHAMAN = { 0.25, 4.13, 0.55, -13.62, 1.68, }, -- 0.3, 0.7, 25.1, 83.4
		ROGUE = { 0.25, -1.00, 0.51, -15.77, -0.53, }, -- 0.2, 0.9, 19.8, 109.3
		MAGE = { 0.17, 11.88, 0.21, 10.08, 3.45, }, -- 0.3, 0.7, 25.0, 38.6
		WARLOCK = { 0.27, 4.16, 0.45, -6.58, 2.06, }, -- 0.7, 0.3, 24.7, 72.5
		PRIEST = { 0.19, 9.96, 0.28, 5.36, 3.17, }, -- 0.3, 1.0, 24.9, 45.4
		HUNTER = { 0.34, -0.55, 0.43, -5.02, -5.44, }, -- 0.3, 0.7, 25.0, 260.8
		DRUID = { 0.16, 1.82, 0.25, -2.95, -1.91, }, -- 0.2, 0.5, 14.4, 99.5
	},
	crit = {
		WARRIOR = { 0.29, 1.77, 1.30, -58.15, 1.14, }, -- 0.2, 0.7, 33.0, 127.5
		PALADIN = { 0.27, 2.46, 0.54, -12.69, 0.66, }, -- 0.2, 0.8, 25.1, 108.9
		SHAMAN = { 0.24, 4.21, 0.55, -13.29, 1.67, }, -- 0.2, 0.7, 25.1, 83.4
		ROGUE = { 0.49, -2.04, 1.17, -41.64, -0.29, }, -- 0.2, 1.2, 40.5, 214.3
		MAGE = { 0.17, 11.98, 0.21, 10.08, 3.46, }, -- 0.3, 0.7, 25.0, 38.5
		WARLOCK = { 0.24, 4.80, 0.46, -7.57, 2.00, }, -- 0.2, 0.7, 24.6, 73.8
		PRIEST = { 0.19, 9.76, 0.28, 5.36, 3.18, }, -- 0.2, 0.7, 24.9, 45.2
		HUNTER = { 0.56, -1.28, 0.69, -8.46, -1.53, }, -- 0.3, 0.8, 40.0, 261.4
		DRUID = { 0.27, 3.06, 0.47, -7.79, 0.96, }, -- 0.2, 0.9, 25.1, 101.5
	},
	spellcrit = {
		WARRIOR = { 0, 0, 0, 0, -1.00, },
		PALADIN = { 0.76, 6.18, 2.20, -72.21, 3.33, }, -- 0.8, 7.6, 81.5, 135.7
		SHAMAN = { 0.93, -0.30, 2.23, -75.21, 2.20, }, -- 0.5, 3.0, 80.9, 226.6
		ROGUE = { 0, 0, 0, 0, -1.00, },
		MAGE = { 1.20, -4.43, 1.03, 5.78, 0.91, }, -- 0.8, 0.1, 78.1, 319.6
		WARLOCK = { 1.02, -2.09, 2.17, -69.16, 1.70, }, -- 0.6, 1.6, 82.6, 272.4
		PRIEST = { 1.15, -4.03, 1.38, -16.78, 1.24, }, -- 0.5, 1.0, 79.9, 300.2
		HUNTER = { 0.87, 7.32, 1.92, -53.80, 3.60, }, -- 0.7, 2.1, 80.6, 112.4
		DRUID = { 1.01, -1.23, 2.00, -59.81, 1.85, }, -- 0.5, 1.1, 80.4, 253.1
	}
}

function GetBaseDataPoint(type, unit, value)
	local info = BASE_INFO[type]
	local numbers = info[unit.class]
	local level = unit.level
	local a, b
	if level < 60 then
		a, b = numbers[1], numbers[2]
	else
		a, b = numbers[3], numbers[4]
	end
	local m = a * level + b
	return (m == 0) and 0 or (value / m + numbers[5])
end

local SPI_BASE_REGEN = {
	0.034965,	0.034191,	0.033465,	0.032526,	0.031661,	0.031076,
	0.030523,	0.029994,	0.029307,	0.028661,	0.027584,	0.026215,
	0.025381,	0.024300,	0.023345,	0.022748,	0.021958,	0.021386,
	0.020790,	0.020121,	0.019733,	0.019155,	0.018819,	0.018316,
	0.017936,	0.017576,	0.017201,	0.016919,	0.016581,	0.016233,
	0.015994,	0.015707,	0.015464,	0.015204,	0.014956,	0.014744,
	0.014495,	0.014302,	0.014094,	0.013895,	0.013724,	0.013522,
	0.013363,	0.013175,	0.012996,	0.012853,	0.012687,	0.012539,
	0.012384,	0.012233,	0.012113,	0.011973,	0.011859,	0.011714,
	0.011575,	0.011473,	0.011342,	0.011245,	0.011110,	0.010999,
	0.010700,	0.010522,	0.010290,	0.010119,	0.009968,	0.009808,
	0.009651,	0.009553,	0.009445,	0.009327,
}

function GetSpiritBaseRegen(unit)
	return SPI_BASE_REGEN[unit.level]
end

local RATINGS = {
	skill_weapon_dagger = 2.5,
	skill_weapon_sword = 2.5,
	skill_weapon_sword_2h = 2.5,
	skill_weapon_axe = 2.5,
	skill_weapon_axe_2h = 2.5,
	skill_weapon_mace = 2.5,
	skill_weapon_mace_2h = 2.5,
	skill_weapon_bow = 2.5,
	skill_weapon_crossbow = 2.5,
	skill_weapon_gun = 2.5,
	skill_weapon_fist = 2.5,
	skill_weapon_staff = 2.5,
	skill_weapon_polearm = 2.5,

	skill_defense = 1.5,
	
	dodge = 12,
	parry = 15,
	block = 5,
	
	meleehit = 10,
	meleecrit = 14,
	meleehaste = 10,
	meleehit_taken = 10,
	
	rangedhit = 10,
	rangedcrit = 14,
	rangedhaste = 10,
	rangedhit_taken = 10,
	
	spellhit = 8,
	spellcrit = 14,
	spellhaste = 10,
	spellhit_taken = 8,
	
	resilience = 25,
	expertise = 2.5,
}

function GetRatingBonus(type, rating, level)
	local mult
	if level < 10 then
		mult = 26
	elseif level <= 60 then
		mult = 52 / (level - 8)
	elseif level <= 70 then
		mult = 3.1951219512195124 - 0.036585365853658534 * level
	end
	return rating * mult / RATINGS[type]
end

function GetArmorMitigation(armor, level)
	if level > 59 then
			level = level + 4.5 * (level - 59)
	end
	local temp = armor / (85 * level + 400)
	local mitigation = temp / (1 + temp)
	if mitigation > 0.75 then
			return 0.75
	end
	if mitigation < 0 then
			return 0
	end
	return mitigation
end

function GetEquipmentAuras(eq)
	local data = {}
	local setcounts = {}
	for slot, info in pairs(eq) do
		local item = info.item
		if item then
			assert(item.slot and (item.slot == slot or item.slot.."1" == slot or item.slot.."2" == slot))
			local res = {}
			res.stat = item.stat
			local set = item.set
			if set then
				setcounts[set] = (setcounts[set] or 0) + 1
			end
			local socket_valid = true
			for i = 1, 3 do
				local socket = "socket"..i
				if item[socket] then
					if info[socket] then
						res[socket] = info[socket].stat
						if not gems.valid(info[socket].color, item[socket]) then
							socket_valid = false
						end
					else
						socket_valid = false
					end
				end
			end
			if socket_valid then
				res.socket_bonus = item.socket_bonus
			else
				print("Warning : invalid sockets for slot", slot)
			end
			local enchant = info.enchant
			if enchant then
				assert(enchants.valid(enchant.slot, slot))
				res.enchant = enchant.stat
			end
			data[slot] = res
		end
	end
	-- sets
	for set, count in pairs(setcounts) do
		local info = sets[set]
		if info then
			for i = 1, count do
				data[set.."_"..i] = sets[set]["bonus"..i]
			end
		end
	end
	return data
end

require("gems")
require("enchants")
require("sets")

function InsertAuras(unit, auras)
	for _, entry in pairs(auras) do
		for type, stats in pairs(entry) do
			if type == "auras" then
				for _, aura in ipairs(stats) do
					unit.auras[#unit.auras + 1] = aura
				end
			else
				for stat, value in pairs(stats) do
					local rating
					if stat:sub(1, 3) == "cr_" then
						rating = true
						stat = stat:sub(4)
					end
					local rstat
					if stat == "hit" or stat == "crit" or stat == "haste" or stat == "attackpower" then
						rstat = "ranged"..stat
						stat = "melee"..stat
					elseif stat == "armor" and type == "stat" then
						stat = "base_armor"
					elseif stat == "manareg" then
						stat = "manareg_fsr"
					end
					unit.auras[#unit.auras + 1] = { type = rating and "rating" or "stat", attribute = stat, value = value }
					if rstat then
						unit.auras[#unit.auras + 1] = { type = rating and "rating" or "stat", attribute = rstat, value = value }
					end
				end
			end
		end
	end
end
